#pragma once


#include <elasticlient/client.h>
#include <cpr/cpr.h>
#include <iostream>
#include <memory>
#include "../../common/log.hpp"
#include <json/json.h>

bool Serialize(const Json::Value& val, std::string& dst)
{
    Json::StreamWriterBuilder swb;
    swb.settings_["emitUTF8"] = true;
    std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

    std::stringstream ss;
    int ret = sw->write(val, &ss);
    if(ret != 0)
    {
        ERROR_LOG("序列化失败");
        return false;
    }
    dst = ss.str();

    return true;
}

bool UnSerialize(const std::string& src, Json::Value& val)
{
    Json::CharReaderBuilder crb;
    std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
    std::string err;
    bool ret = cr->parse(src.c_str(), src.c_str() + src.size(), &val, &err);
    if(ret == 0)
    {
        ERROR_LOG("反序列化失败{}", err);
        return false;
    }

    return true;
}


//创建索引
class EsIndex
{
public:
    EsIndex(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "doc")
        : _name(name)
        , _client(client)
        , _type(type)
    {
        Json::Value analysis;
        Json::Value analyzer;
        Json::Value ik;
        Json::Value tokenizer;


        tokenizer["tokenizer"] = "ik_max_word";
        ik["tokenizer"] = tokenizer;
        analyzer["analyzer"] = ik;
        analysis["analysis"] = analyzer;
        _index["setting"] = analysis;


    }

    void append(const std::string& key, 
                const std::string& type = "text", 
                const std::string& analyzer =  "ik_max_word", 
                bool enable = true)
    {
        Json::Value fields;

        fields["type"] = type;
        fields["analyzer"] = analyzer;
        fields["enable"] = enable;

        _properties[key] = fields;
    }

    bool create(const std::string& id = "default_id")
    {
        Json::Value mappings;
        mappings["dynamic"] = true;
        mappings["properties"] = _properties;
        _index["mappings"] = mappings;

        std::string body;
        bool ret = Serialize(_index, body);
        if(ret == 0)
        {
            ERROR_LOG("创建索引序列化失败");
            return false;
        }

        // INFO_LOG("请求正文成功");
        // std::cout << body << std::endl;
        
        try
        {
            auto rsp = _client->index(_name, _type, "", body);
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                ERROR_LOG("创建ES索引{}失败 {}", _name, rsp.status_code);
                return false;
            }
        }
        catch(std::exception& e)
        {
            ERROR_LOG("创建ES索引{}失败 {}", _name, e.what());
            return false;
        }

        INFO_LOG("创建ES索引成功");

        return true;
    }


private:
    std::string _name;
    std::string _type;
    Json::Value _index;
    Json::Value _properties;
    std::shared_ptr<elasticlient::Client> _client;
};


// 添加数据
class EsInsert
{
public:
    EsInsert(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "doc")
        : _name(name)
        , _client(client)
        , _type(type)
    {}

    void append(const std::string& key, const std::string& val)
    {
        _item[key] = val;
    }

    bool insert(std::string id = "")
    {
        std::string body;
        bool ret = Serialize(_item, body);
        if(ret == 0)
        {
            ERROR_LOG("添加数据 序列化失败");
            return false;
        }
        
        try
        {
            auto rsp = _client->index(_name, _type, id, body);
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                ERROR_LOG("新增数据{}失败 {}", body, rsp.status_code);
                return false;
            }
        }
        catch(std::exception& e)
        {
            ERROR_LOG("新增数据{}失败 {}", body, e.what());
            return false;
        }

        INFO_LOG("新增数据成功");
        return true;
    }
private:
    std::string _name;
    std::string _type;
    std::string _id;
    Json::Value _item;
    std::shared_ptr<elasticlient::Client> _client;
};

// 删除数据
class EsRemove
{
public:
    EsRemove(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type)
        : _name(name)
        , _client(client)
        , _type(type)
    {}

    bool remove(const std::string id)
    {

        try
        {
            auto rsp = _client->remove(_name, _type, id);
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                ERROR_LOG("删除数据{}失败 {}", id, rsp.status_code);
                return false;
            }
        }
        catch(std::exception &e)
        {
            ERROR_LOG("删除数据{}失败 {}", id, e.what());
            return false;
        }

        INFO_LOG("数据删除成功");
        return true;
    }
private:
    std::string _name;
    std::string _type;
    std::string _id;
    std::shared_ptr<elasticlient::Client> _client;
};


//搜索数据
class EsSearch
{
public:
    EsSearch(std::shared_ptr<elasticlient::Client>& client, const std::string& name, const std::string& type = "doc")
        : _name(name)
        , _client(client)
        , _type(type)
    {}

    void append_must_not_terms(const std::string& key, const std::vector<std::string>& vals)
    {
        Json::Value fields;
        for(auto val : vals)
        {
            fields[key].append(val);
        }
        Json::Value terms;
        terms["terms"] = fields;
        _must_not.append(terms);
    }

    void append_should_terms(const std::string& key, const std::string& val)
    {
        Json::Value fields;
        fields[key] = val;
        Json::Value match;
        match["match"] = fields;

        _should.append(match);
    }

    Json::Value search()
    {
        Json::Value cond;
        if(_must_not.empty() == false)
            cond["must_not"] = _must_not;

        if(_should.empty() == false)
            cond["should"] = _should;
        
        Json::Value query;
        query["bool"] = cond;
        std::string body;

        Json::Value root;
        root["query"] = query;

        bool ret = Serialize(root, body);
        if(ret == 0)
        {
            ERROR_LOG("索引序列化失败");
            return Json::Value();
        }
        std::cout << body << std::endl;
        cpr::Response rsp;
        try
        {
            rsp = _client->search(_name, _type, body);
            if(rsp.status_code < 200 || rsp.status_code >= 300)
            {
                ERROR_LOG("检索数据{}失败 {}", body, rsp.status_code);
                return Json::Value();
            }
        }
        catch(std::exception& e)
        {
            ERROR_LOG("检索数据{}失败 {}", body, e.what());
            return Json::Value();
        }

        Json::Value json_res;
        ret = UnSerialize(rsp.text, json_res);
        if(ret == false)
        {
            ERROR_LOG("检索数据{} 反序列化失败", rsp.text);
            return Json::Value();
        }
        return json_res["hits"]["hits"];
    }

private:
    std::string _name;
    std::string _type;
    Json::Value _must_not;
    Json::Value _should;
    std::shared_ptr<elasticlient::Client> _client;
};